/** @file draw.c
    @brief Implementations for drawing ...

    @author Juan Pablo Carbajal
    
    @date 21. July, 2008
    

*/
#include "draw.h"
// Locals
cpVect mousePoint;

static void
drawCircle(cpFloat x, cpFloat y, cpFloat r, cpFloat a)
{
	const int segs = 15;
	const cpFloat coef = 2.0*M_PI/(cpFloat)segs;
	
	glBegin(GL_LINE_STRIP); {
		for(int n = 0; n <= segs; n++){
			cpFloat rads = n*coef;
			glVertex2f(r*cos(rads + a) + x, r*sin(rads + a) + y);
		}
		glVertex2f(x,y);
	} glEnd();
}

static void
drawCircleShape(cpShape *shape)
{
	cpBody *body = shape->body;
	cpCircleShape *circle = (cpCircleShape *)shape;
	cpVect c = cpvadd(body->p, cpvrotate(circle->c, body->rot));
	drawCircle(c.x, c.y, circle->r, body->a);
}

static void
drawSegmentShape(cpShape *shape)
{
	cpBody *body = shape->body;
	cpSegmentShape *seg = (cpSegmentShape *)shape;
	cpVect a = cpvadd(body->p, cpvrotate(seg->a, body->rot));
	cpVect b = cpvadd(body->p, cpvrotate(seg->b, body->rot));
	
	glBegin(GL_LINES); {
		glVertex2f(a.x, a.y);
		glVertex2f(b.x, b.y);
	} glEnd();
}

static void
drawPolyShape(cpShape *shape)
{
	cpBody *body = shape->body;
	cpPolyShape *poly = (cpPolyShape *)shape;
	
	int num = poly->numVerts;
	cpVect *verts = poly->verts;
	
	glBegin(GL_LINE_LOOP);
	for(int i=0; i<num; i++){
		cpVect v = cpvadd(body->p, cpvrotate(verts[i], body->rot));
		glVertex2f(v.x, v.y);
	} glEnd();
}

static void
drawObject(void *ptr, void *unused)
{
	cpShape *shape = (cpShape *)ptr;
	
	switch(shape->klass->type){
		case CP_CIRCLE_SHAPE:
			drawCircleShape(shape);
			break;
		case CP_SEGMENT_SHAPE:
			drawSegmentShape(shape);
			break;
		case CP_POLY_SHAPE:
			drawPolyShape(shape);
			break;
		default:
			printf("Bad enumeration in drawObject().\n");
	}
}

static void
drawBB(void *ptr, void *unused)
{
	cpShape *shape = (cpShape *)ptr;

	glBegin(GL_LINE_LOOP); {
		glVertex2f(shape->bb.l, shape->bb.b);
		glVertex2f(shape->bb.l, shape->bb.t);
		glVertex2f(shape->bb.r, shape->bb.t);
		glVertex2f(shape->bb.r, shape->bb.b);
	} glEnd();
}

static void
drawCollisions(void *ptr, void *data)
{
	cpArbiter *arb = (cpArbiter *)ptr;
	for(int i=0; i<arb->numContacts; i++){
		cpVect v = arb->contacts[i].p;
		glVertex2f(v.x, v.y);
	}
}

static void 
pickingFunc(cpShape *shape, void *data)
{
	drawObject(shape, NULL);
}

void
display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	
	glColor3f(0.6, 1.0, 0.6);
	//cpSpaceHashEach(space->activeShapes, &drawBB, NULL);
	//cpSpaceHashEach(space->staticShapes, &drawBB, NULL);
	
	glColor3f(0.0, 0.0, 0.0);
	cpSpaceHashEach(space->activeShapes, &drawObject, NULL);
	cpSpaceHashEach(space->staticShapes, &drawObject, NULL);

//	glColor3f(1.0, 0.0, 0.0);
//	cpSpaceShapePointQuery(space, mousePoint, pickingFunc, NULL);
//	cpSpaceStaticShapePointQuery(space, mousePoint, pickingFunc, NULL);

	cpArray *bodies = space->bodies;
	int num = bodies->num;
//	cpVect gc;

	glBegin(GL_LINES); {
		glColor3f(1.0, 0.0, 1.0);
		for(int i=0; i<num; i++)
		{
		  cpBody *body = (cpBody *)bodies->arr[i];
    	  Sing *aux=(Sing*)((ActorData*)body->data)->singularity;
		  
		  // Draw the angle of singularities
		  for(int j=0;j<aux->Nsing;j++)
		  {
			if(!strcmp(aux->sing[j].type,"magdipole"))
	        {
			   cpVect a=cpvmult(cpvforangle(aux->Gangle[j]),7);
			   glVertex2f(aux->Gpos[j].x - a.x,aux->Gpos[j].y - a.y);
			   glVertex2f(aux->Gpos[j].x + a.x,aux->Gpos[j].y + a.y);
				
			   glVertex2f(aux->Gpos[j].x + a.y/2,aux->Gpos[j].y - a.x/2);
			   glVertex2f(aux->Gpos[j].x + a.x,aux->Gpos[j].y + a.y);
				
			   glVertex2f(aux->Gpos[j].x - a.y/2,aux->Gpos[j].y + a.x/2);
			   glVertex2f(aux->Gpos[j].x + a.x,aux->Gpos[j].y + a.y);
		    }
			
		  }
		 }
	} glEnd();
	
	glBegin(GL_POINTS); {
//		glColor3f(0.0, 0.0, 1.0);
		for(int i=0; i<num; i++)
		{
			
			cpBody *body = (cpBody *)bodies->arr[i];
			
			Sing *aux=(Sing*)((ActorData*)body->data)->singularity;
			
			glColor3f(0.0, 0.0, 1.0);

			glVertex2f(body->p.x, body->p.y);
			
			// Draw the singularities
		    for(int j=0;j<aux->Nsing;j++)
			{
			    if(aux->sing[j].value>0.0)
			      glColor3f(1.0, 0.3, 0.3);
			    else
			      glColor3f(0.3, 1.0, 0.3);
	          
				//Global coordinates of charges
			    glVertex2f(aux->Gpos[j].x, aux->Gpos[j].y);
				
			}
			
		}
		glColor3f(1.0, 0.0, 0.0);
		cpArrayEach(space->arbiters, &drawCollisions, NULL);
	} glEnd();
	
	glutSwapBuffers();
	ticks++;
	run();
	
}

static void
removeShapeBody(cpShape *shape, void *data)
{
	cpSpaceRemoveBody(space, shape->body);
	cpBodyFree(shape->body);

	cpSpaceRemoveShape(space, shape);
	cpShapeFree(shape);
}

static void
timercall(int value)
{
	glutTimerFunc(SLEEP_TICKS, timercall, 0);
		
	glutPostRedisplay();
}

static void
idle(void)
{
	glutPostRedisplay();
}

static void
initGL(void)
{
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glPointSize(3.0);
	
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
	glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
	glLineWidth(2.5f);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-320.0, 320.0, -240.0, 240.0, -1.0, 1.0);
	glTranslatef(0.5, 0.5, 0.0);
}
static void 
processNormalKeys(unsigned char key, int x, int y) 
{
	if (key == 'q' || key =='Q') 
		exit(0);
	/** @todo Set "p" for pause */
}

void
glutStuff(int argc, const char *argv[], performer* performer)
{
	/** @todo Make this func fancier */
	glutInit(&argc, (char**)argv);
	
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	
	glutInitWindowSize(performer->getRender()->getFirstWindow()->getWidth(),
			performer->getRender()->getFirstWindow()->getHeight());
	glutCreateWindow(performer->getRender()->getFirstWindow()->getName().c_str());
	
	initGL();

	glutDisplayFunc(display);
	
	glutIdleFunc(idle);
	
	glutTimerFunc(SLEEP_TICKS, timercall, 0);
	
	glutKeyboardFunc(processNormalKeys);

	glutMainLoop();
}
